home *** CD-ROM | disk | FTP | other *** search
/ Inter.Net 55-1 / Inter.Net 55-1.iso / CBuilder / Setup / BCB / data.z / ipcthrd.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-02-09  |  24.9 KB  |  929 lines

  1. //----------------------------------------------------------------------------
  2. //Borland C++Builder
  3. //Copyright (c) 1987, 1998 Borland International Inc. All Rights Reserved.
  4. //----------------------------------------------------------------------------
  5. //-----------------------------------------------------------------------------
  6. #include "IPCThrd.h"
  7. #include <stdio.h>
  8. //-----------------------------------------------------------------------------
  9. /* Inter-Process Communication Thread Classes */
  10.  
  11. // Utility Routines
  12.  
  13. void __fastcall Error(const AnsiString Msg)
  14. {
  15.   ShowMessage(Msg);
  16.   throw(Msg);
  17. }
  18.  
  19. AnsiString __fastcall EventName(TEventKind Event)
  20. {
  21.   switch (Event){
  22.     case evMonitorAttach: return "evMonitorAttach";
  23.     case evMonitorDetach: return "evMonitorDetach";
  24.     case evMonitorSignal: return "evMonitorSignal";
  25.     case evMonitorExit:   return "evMonitorExit";
  26.     case evClientStart:   return "evClientStart";
  27.     case evClientStop:    return "evClientStop";
  28.     case evClientAttach:  return "evClientAttach";
  29.     case evClientDetach:  return "evClientDetach";
  30.     case evClientSwitch:  return "evClientSwitch";
  31.     case evClientSignal:  return "evClientSignal";
  32.     case evClientExit:    return "evClientExit";
  33.     default:              return "";
  34.   }
  35. }
  36.  
  37. /* Utility function used by the monitor to determine if another monitor is
  38.   already running.  This is needed to make the monitor a single instance .EXE.
  39.   This function relies on the fact that the first 4 bytes of the client
  40.   directory always contain the Application handle of the monitor, or zero if
  41.   no monitor is running.  This function is used in Monitor.cpp. */
  42.  
  43. bool __fastcall IsMonitorRunning(THandle& Hndl)
  44. {
  45.    TSharedMem*  SharedMem;
  46.    bool      Result;
  47.  
  48.    SharedMem = new TSharedMem(CLIENT_DIR_NAME, 4);
  49.    Hndl = *((THandle*)(SharedMem->Buffer));
  50.    Result = (Hndl != 0);
  51.    delete SharedMem;
  52.    return Result;
  53. }
  54.  
  55. /* THandledObject */
  56.  
  57. /* This is a generic class for all encapsulated WinAPI's which need to call
  58.   CloseHandle when no longer needed.  This code eliminates the need for
  59.   3 identical destructors in the TEvent, TMutex, and TSharedMem classes
  60.   which are descended from this class. */
  61.  
  62. __fastcall  THandledObject::~THandledObject()
  63. {
  64.    if (FHandle != 0)
  65.       CloseHandle(FHandle);
  66. }
  67.  
  68. /* TEvent */
  69.  
  70. /* This class encapsulates the concept of a Win32 event (not to be
  71.   confused with Delphi events), see "CreateEvent" in the Win32
  72.   reference for more information */
  73. __fastcall TEvent::TEvent(const AnsiString Name, bool Manual)
  74. {
  75.    AnsiString buf;
  76.  
  77.    buf = Name + ".Data";
  78.    FHandle = CreateEvent(NULL, Manual, false, buf.c_str());
  79.    if (FHandle == 0) Abort();
  80. }
  81.  
  82. void __fastcall TEvent::Reset()
  83. {
  84.    ResetEvent(FHandle);
  85. }
  86.  
  87. void __fastcall TEvent::Signal()
  88. {
  89.    SetEvent(FHandle);
  90. }
  91.  
  92. bool __fastcall TEvent::Wait(int TimeOut)
  93. {
  94.    return WaitForSingleObject(FHandle, TimeOut) == WAIT_OBJECT_0;
  95. }
  96.  
  97. /* TMutex */
  98.  
  99. /* This class encapsulates the concept of a Win32 mutex.  See "CreateMutex"
  100.   in the Win32 reference for more information */
  101.  
  102. __fastcall TMutex::TMutex(const AnsiString Name)
  103. {
  104.    FHandle = CreateMutex(NULL, false, Name.c_str());
  105.    if (FHandle == 0) Abort();
  106. }
  107.  
  108. bool __fastcall TMutex::Get(int TimeOut)
  109. {
  110.    return WaitForSingleObject(FHandle, TimeOut) == WAIT_OBJECT_0;
  111. }
  112.  
  113. bool __fastcall TMutex::Release(void)
  114. {
  115.    return ReleaseMutex(FHandle);
  116. }
  117.  
  118. /* TSharedMem */
  119.  
  120. /* This class simplifies the process of creating a region of shared memory.
  121.   In Win32, this is accomplished by using the CreateFileMapping and
  122.   MapViewOfFile functions. */
  123.  
  124. __fastcall TSharedMem::TSharedMem(const AnsiString Name, int Size)
  125. {
  126.  
  127.    try{
  128.  
  129.     FName = Name;
  130.     FSize = Size;
  131.  
  132.     /* CreateFileMapping, when called with $FFFFFFFF for the handle value,
  133.       creates a region of shared memory. If an object with same name already
  134.       exists, then a Handle to that object will be returned. */
  135.     FHandle = CreateFileMapping((HANDLE)0xFFFFFFFF,
  136.                                 NULL,
  137.                                 PAGE_READWRITE,
  138.                                 0,
  139.                                 Size,
  140.                                 Name.c_str());
  141.     if (FHandle == 0) Abort();
  142.     FCreated = (GetLastError == 0);
  143.     /* We still need to map a pointer to the handle of the shared memory region */
  144.     FFileView = MapViewOfFile(FHandle,
  145.                               FILE_MAP_WRITE,
  146.                               0, 0, Size);
  147.     if (FFileView == NULL) Abort();
  148.   }
  149.   catch(...) {
  150.     Error(Format("Error creating shared memory %s (%d)",
  151.                  (TVarRec*)Name.c_str(), GetLastError()));
  152.   }
  153. }
  154.  
  155. __fastcall TSharedMem::~TSharedMem()
  156. {
  157.    if (FFileView != NULL)
  158.      UnmapViewOfFile(FFileView);
  159. }
  160.  
  161. /* Debug Tracing */
  162.  
  163. #if defined(IPCDEBUG)
  164.  
  165. /* Debug Tracing */
  166.  
  167. /* The IPCTracer class was used to create and debug the IPC classes which
  168.   follow.  When developing a multi-process, multi-threaded application, it
  169.   is difficult to debug effectively using ordinary debuggers.  The trace
  170.   data is displayed in a Window when you click on a speed button in the
  171.   monitor program. */
  172.  
  173.   /* TIPCTracer */
  174.  
  175. __fastcall TIPCTracer::TIPCTracer(AnsiString ID)
  176. {
  177.    strcpy(FIDName, ID.c_str());
  178.    FSharedMem = new TSharedMem(TRACE_BUFFER, TRACE_BUF_SIZE);
  179.    FMutex = new TMutex(TRACE_MUTEX);
  180.  
  181.    //The first four bytes of the buffer contain the number of meaningful
  182.    //bytes in the buffer at any given time.
  183.    if (*((int*)(FSharedMem->Buffer)) == 0)
  184.      *(int*)FSharedMem->Buffer = sizeof(PTraceEntry);
  185. }
  186.  
  187. __fastcall TIPCTracer::~TIPCTracer()
  188. {
  189.    delete FMutex;
  190.    delete FSharedMem;
  191. }
  192.  
  193. PTraceEntry __fastcall TIPCTracer::MakePtr(int Ofs)
  194. {
  195.    return PTraceEntry(int(FSharedMem->Buffer) + Ofs);
  196. }
  197.  
  198. PTraceEntry __fastcall TIPCTracer::FirstEntry()
  199. {
  200.    return MakePtr(sizeof(PTraceEntry));
  201. }
  202.  
  203. PTraceEntry __fastcall TIPCTracer::NextEntry(void)
  204. {
  205.    return MakePtr(*(int*)(FSharedMem->Buffer));
  206. }
  207.  
  208. void __fastcall TIPCTracer::Add(PChar AMsg)
  209. {
  210.    PTraceEntry    TraceEntry;
  211.    int            EntrySize;
  212.    TLargeInteger  TempTime;
  213.  
  214.    FMutex->Get(INFINITE);
  215.    TraceEntry = NextEntry();
  216.    EntrySize = sizeof(AMsg) + sizeof(TTraceEntry) + 16;
  217.  
  218.   /* If we hit the end of the buffer, just wrap around */
  219.    if ((EntrySize + *(int*)(FSharedMem->Buffer))  >  FSharedMem->Size)
  220.       TraceEntry = FirstEntry();
  221.  
  222.    QueryPerformanceCounter(&TempTime);
  223. //  TraceEntry->Time = TempTime.LowPart;
  224.    TraceEntry->Time = (int) TempTime.QuadPart;
  225.    TraceEntry->Size = EntrySize;
  226.    sprintf(TraceEntry->Msg,
  227.           "%s: %s", FIDName, AMsg);
  228.    *(int*)(FSharedMem->Buffer) = *(int*)(FSharedMem->Buffer) + TraceEntry->Size;
  229.    FMutex->Release();
  230. }
  231.  
  232. void __fastcall TIPCTracer::GetList(TStrings *List)
  233. {
  234.    PTraceEntry  LastEntry;
  235.    PTraceEntry TraceEntry;
  236.    int  Dif;
  237.    int  LastTime;
  238.  
  239.    List->BeginUpdate();
  240.    try{
  241.     LastEntry = NextEntry();
  242.     TraceEntry = FirstEntry();
  243.     LastTime = TraceEntry->Time;
  244.     List->Clear();
  245.     while (TraceEntry != LastEntry){
  246.       Dif = TraceEntry->Time - LastTime;
  247.       List->Add(Format("%x %10d %s",
  248.                        ARRAYOFCONST((TraceEntry->Time,
  249.                                      Dif,
  250.                                      TraceEntry->Msg))));
  251.       LastTime = TraceEntry->Time;
  252.       (int)TraceEntry = (int)TraceEntry + TraceEntry->Size;
  253.     }
  254.    }
  255.    catch(...){
  256.     List->EndUpdate();
  257.     throw;
  258.    }
  259.    List->EndUpdate();
  260. }
  261.  
  262. void __fastcall TIPCTracer::Clear()
  263. {
  264.    FMutex->Get(INFINITE);
  265.    *(int*)FSharedMem->Buffer = sizeof(PTraceEntry);
  266.    FMutex->Release();
  267. }
  268.  
  269. #endif
  270.  
  271. /* TIPCEvent */
  272.  
  273. /* Win32 events are very basic.  They are either signaled or non-signaled.
  274.   The TIPCEvent class creates a "typed" TEvent, by using a block of shared
  275.   memory to hold an "EventKind" property.  The shared memory is also used
  276.   to hold an ID, which is important when running multiple clients, and
  277.   a Data area for communicating data along with the event */
  278.  
  279. __fastcall TIPCEvent::TIPCEvent(TIPCThread *AOwner,
  280.                                 const AnsiString Name,
  281.                                 bool Manual)
  282.                     :TEvent(Name, Manual)
  283. {
  284.    FOwner = AOwner;
  285.    FSharedMem = new TSharedMem(Name, sizeof(TIPCEventInfo));
  286.    FEventInfo = (TIPCEventInfo*) FSharedMem->Buffer;
  287. }
  288.  
  289. __fastcall TIPCEvent::~TIPCEvent()
  290. {
  291.    delete FSharedMem;
  292. }
  293.  
  294. int __fastcall TIPCEvent::GetID()
  295. {
  296.    return FEventInfo->FID;
  297. }
  298.  
  299. void __fastcall TIPCEvent::SetID(int Value)
  300. {
  301.    FEventInfo->FID = Value;
  302. }
  303.  
  304. TEventKind __fastcall TIPCEvent::GetKind()
  305. {
  306.    return FEventInfo->FKind;
  307. }
  308.  
  309. void __fastcall TIPCEvent::SetKind(TEventKind Value)
  310. {
  311.    FEventInfo->FKind = Value;
  312. }
  313.  
  314. TEventData __fastcall TIPCEvent::GetData()
  315. {
  316.    return FEventInfo->FData;
  317. }
  318.  
  319. void __fastcall TIPCEvent::SetData(const TEventData& Value)
  320. {
  321.    FEventInfo->FData = Value;
  322. }
  323.  
  324. void __fastcall TIPCEvent::Signal(TEventKind Kind)
  325. {
  326.    SetID(OwnerID);
  327.    FEventInfo->FKind = Kind;
  328.    TEvent::Signal();
  329. }
  330.  
  331. void __fastcall TIPCEvent::SignalID(TEventKind Kind, int ID)
  332. {
  333.    FEventInfo->FID = ID;
  334.    FEventInfo->FKind = Kind;
  335.    TEvent::Signal();
  336. }
  337.  
  338. void __fastcall TIPCEvent::SignalData(TEventKind Kind,
  339.                                       int ID,
  340.                                       const TEventData& Data)
  341. {
  342.    FEventInfo->FID = ID;
  343.    FEventInfo->FData = Data;
  344.    FEventInfo->FKind = Kind;
  345.    TEvent::Signal();
  346. }
  347.  
  348. bool __fastcall TIPCEvent::WaitFor(int TimeOut,
  349.                                       int ID,
  350.                                       TEventKind Kind)
  351. {
  352.    bool returnval = Wait(TimeOut);
  353.    if (returnval)
  354.       returnval = (ID == FEventInfo->FID) && (Kind == FEventInfo->FKind);
  355.    else
  356.       FOwner->DbgStr(Format("Wait Failed %s Kind: %s ID: %x" ,
  357.                             ARRAYOFCONST((FOwner->ClassName(),
  358.                                           EventName(Kind),
  359.                                           ID))));
  360.    return returnval;
  361. }
  362.  
  363. /* TClientDirectory */
  364.  
  365. /* The client directory is a block of shared memory where the list of all
  366.   active clients is maintained */
  367.  
  368. __fastcall TClientDirectory::TClientDirectory(int MaxClients)
  369. {
  370.    FMaxClients = MaxClients;
  371.    FMutex = new TMutex(CLIENT_DIR_MUTEX);
  372.    FSharedMem = new TSharedMem(CLIENT_DIR_NAME,
  373.                               FMaxClients * sizeof(TClientDirEntry) + 8);
  374.    FMonitorID = (int*) FSharedMem->Buffer;
  375.    (int)FClientCount = (int)FMonitorID + sizeof(FMonitorID);
  376.    (int)FDirBuffer = (int)FClientCount + sizeof(FClientCount);
  377. }
  378.  
  379. __fastcall TClientDirectory::~TClientDirectory()
  380. {
  381.    delete FSharedMem;
  382. }
  383.  
  384. int __fastcall TClientDirectory::AddClient(int ClientID, const AnsiString AName)
  385. {
  386.    int Result = -1;
  387.    if (Count == FMaxClients)
  388.       {
  389.       String EString("Maximun of ");
  390.       EString += (int)FMaxClients;
  391.       EString += " clients allowed.";
  392.       Error(EString);
  393.       }
  394.    if (IndexOf(ClientID) > -1)
  395.       Error("Duplicate client ID");
  396.    if (FMutex->Get(TIMEOUT)){
  397.     try{
  398.         FDirBuffer[Count]->ID = ClientID;
  399.         lstrcpy(FDirBuffer[Count]->Name,
  400.                 AName.c_str());
  401.         (*FClientCount)++;
  402.         Result = Count-1;
  403.     }
  404.     catch(...){
  405.       FMutex->Release();
  406.       throw;
  407.     } //end catch
  408.    }   //end if (FMutex..
  409.    FMutex->Release();
  410.    return Result;
  411. }
  412.  
  413. int __fastcall TClientDirectory::GetCount()
  414. {
  415.    return (*FClientCount);
  416. }
  417.  
  418. TClientDirEntry __fastcall TClientDirectory::GetClientRec(int Index)
  419. {
  420.    if ((Index < 0) || (Index >= Count))
  421.      Error("Invalid client list index");
  422.    return *FDirBuffer[Index];
  423. }
  424.  
  425. AnsiString __fastcall TClientDirectory::GetClientName(int ClientID)
  426. {
  427.    int  Index;
  428.  
  429.    Index = IndexOf(ClientID);
  430.    if (Index >= 0)
  431.      return FDirBuffer[Index]->Name;
  432.    else
  433.      return "";
  434. }
  435.  
  436. int __fastcall TClientDirectory::IndexOf(int ClientID)
  437. {
  438.    int  I;
  439.  
  440.    for(I=0; I < Count; I++)
  441.    if (FDirBuffer[I]->ID == ClientID){
  442.         return I;
  443.    }
  444.    return -1;
  445. }
  446.  
  447. int __fastcall TClientDirectory::Last()
  448. {
  449.    if (Count > 0)
  450.      return FDirBuffer[Count-1]->ID;
  451.    else
  452.      return 0;
  453. }
  454.  
  455. bool __fastcall TClientDirectory::RemoveClient(int ClientID)
  456. {
  457.    int  Index;
  458.    bool  Result;
  459.    Index = IndexOf(ClientID);
  460.    if ((Index > -1) && FMutex->Get(TIMEOUT)){
  461.      try{
  462.        if ((Index > 0) && (Index < Count))
  463.          Move(FDirBuffer[Index+1],
  464.               FDirBuffer[Index],
  465.              (Count - Index) * sizeof(TClientDirEntry));
  466.        (*FClientCount)--;    //Decrement client count
  467.        Result = true;
  468.      }
  469.     catch(...){
  470.        FMutex->Release();
  471.        throw;
  472.      }
  473.      FMutex->Release();
  474.    } //end if
  475.    else
  476.       Result = false;
  477.    return Result;
  478. }
  479.  
  480. int __fastcall TClientDirectory::GetMonitorID()
  481. {
  482.    return *FMonitorID;
  483. }
  484.  
  485. void __fastcall TClientDirectory::SetMonitorID(int MonitorID)
  486. {
  487.    *FMonitorID = MonitorID;
  488. }
  489.  
  490. /* TIPCThread */
  491.  
  492. /* The TIPCThread class implements the functionality which is common between
  493.   the monitor and client thread classes. */
  494.  
  495. __fastcall TIPCThread::TIPCThread(int AID, const AnsiString AName)
  496.                       :TThread(true)
  497. {
  498.    FID = AID;
  499.    FName = AName;
  500. #if defined(IPCDEBUG)
  501.    if (dynamic_cast<TIPCMonitor*>(this) != 0)
  502.       FTracer = new TIPCTracer(FName);
  503.    else
  504.       FTracer = new TIPCTracer(IntToHex(FID, 8));
  505. #endif
  506.    FMonitorEvent = new TIPCEvent(this, MONITOR_EVENT_NAME, false);
  507.    FClientEvent = new TIPCEvent(this, CLIENT_EVENT_NAME, false);
  508.    FConnectEvent = new TIPCEvent(this, CONNECT_EVENT_NAME, false);
  509.    FClientDirectory = new TClientDirectory(MAX_CLIENTS);
  510. }
  511.  
  512. __fastcall TIPCThread::~TIPCThread()
  513. {
  514.    DeActivate();
  515.    delete FClientDirectory;
  516.    delete FClientEvent;
  517.    delete FMonitorEvent;
  518.    FState = stInActive;
  519. #if defined(IPCDEBUG)
  520.    delete FTracer;
  521. #endif
  522. }
  523.  
  524. /* This procedure is called all over the place to keep track of what is
  525.   going on */
  526.  
  527. void __fastcall TIPCThread::DbgStr(const AnsiString S)
  528. {
  529. #if defined(IPCDEBUG)
  530.    FTracer->Add(S.c_str());
  531. #endif
  532. }
  533.  
  534. /* TIPCMonitor */
  535.  
  536. __fastcall TIPCMonitor::TIPCMonitor(int AID, const AnsiString AName)
  537.                       : TIPCThread(AID, AName),
  538.                         FAutoSwitch(true)
  539.  
  540. {
  541. }
  542.  
  543. void __fastcall TIPCMonitor::Activate()
  544. {
  545.    if (FState == stInActive) {
  546.     // Put the monitor handle into the client directory so we can use it to
  547.     //  prevent multiple monitors from running
  548.     if (FClientDirectory->MonitorID == 0)
  549.       FClientDirectory->MonitorID = FID;
  550.     else
  551.         throw EMonitorActive("Monitor is already active.");
  552.     FState = stDisconnected;
  553.     Resume();
  554.    }
  555. }
  556.  
  557. void __fastcall TIPCMonitor::DeActivate()
  558. {
  559.    if ((State != stInActive) && !(Suspended)){
  560.     FClientDirectory->MonitorID = 0;
  561.     FMonitorEvent->Signal(evMonitorExit);
  562.     if (WaitForSingleObject((void *)Handle, TIMEOUT) != WAIT_OBJECT_0)
  563.         TerminateThread((void *)Handle, 0);
  564.    }
  565. }
  566.  
  567. /* This method, and the TIPCClient.Execute method represent the meat of this
  568.   program.  These two thread handlers are responsible for communcation with
  569.   each other through the IPC event classes */
  570.  
  571. void __fastcall TIPCMonitor::Execute()
  572. {
  573.    int WaitResult;
  574.    bool bContinueThread = true;
  575.    DbgStr(FName + " Activated");
  576.  
  577.    if (FClientDirectory->Count > 0)
  578.      FMonitorEvent->SignalID(evClientStart, FClientDirectory->Last());
  579.    while (bContinueThread == true){
  580.     try {
  581.       WaitResult = WaitForSingleObject(FMonitorEvent->Handle, INFINITE);
  582.       if (WaitResult >= WAIT_ABANDONED)
  583.             DisconnectFromClient(false);
  584.       else{
  585.         if (WaitResult == WAIT_OBJECT_0)
  586.         {
  587.           DbgStr("Event Signaled: " + EventName(FMonitorEvent->Kind));
  588.           switch (FMonitorEvent->Kind){
  589.             case evClientSignal:
  590.               DoOnSignal();
  591.               break;
  592.             case evClientStart:
  593.               if ((AutoSwitch == true) || (FClientID == 0))
  594.                 ConnectToClient(FMonitorEvent->ID);
  595.               DoOnDirUpdate();
  596.               break;
  597.             case evClientStop:
  598.               DoOnDirUpdate();
  599.               break;
  600.             case evClientDetach:
  601.               DisconnectFromClient(false);
  602.               Sleep(100);
  603.               if (AutoSwitch != false)
  604.                 ConnectToClient(FClientDirectory->Last());
  605.               break;
  606.             case evClientSwitch:
  607.               ConnectToClient(FMonitorEvent->ID);
  608.               break;
  609.             case evMonitorExit:
  610.               DisconnectFromClient(false);
  611.               bContinueThread = false;
  612.               break;
  613.             default:
  614.               break;
  615.           } //end switch
  616.         } //end if
  617.         else {
  618.           DbgStr(Format("Unexpected Wait Return Code: %d",
  619.                         ARRAYOFCONST((WaitResult))));
  620.         } //end else
  621.       } //end else
  622.     } //end try
  623.     catch(Exception& E){
  624.       DbgStr(Format("Exception raised in Thread Handler: %s at %X",
  625.                     ARRAYOFCONST((E.Message, ExceptAddr))));
  626.     } //end catch
  627.    }//end while
  628.    FState = stInActive;
  629.    DbgStr("Thread Handler Exited");
  630. }
  631.  
  632. void __fastcall TIPCMonitor::ConnectToClient(int ID)
  633. {
  634.    if (ID == FClientID) return;
  635.    if (FState == stConnected)
  636.       DisconnectFromClient(true);
  637.    if (ID == 0) return;
  638.    DbgStr(Format("Sending evMonitorAttach: %X",
  639.                 ARRAYOFCONST((ID))));
  640.  
  641.    /* Tell a client we want to attach to them */
  642.    FConnectEvent->SignalID(evMonitorAttach, ID);
  643.  
  644.    /* Wait for the client to say "OK" */
  645.    if ((FMonitorEvent->WaitFor(TIMEOUT, ID, evClientAttach) != false) &&
  646.      (FMonitorEvent->Data.Flag == cfAttach)) {
  647.       FClientID = ID;
  648.       FState = stConnected;
  649.       if (FOnConnect != NULL)
  650.         OnConnect(this, true);
  651.       DbgStr("ConnectToClient Successful");
  652.   }
  653.   else
  654.       DbgStr("ConnectToClient Failed: " + EventName(FMonitorEvent->Kind));
  655. }
  656.  
  657. /* If Wait is true ... */
  658.  
  659. void __fastcall TIPCMonitor::DisconnectFromClient(bool Wait)
  660. {
  661.    if (FState == stConnected){
  662.     DbgStr(Format("Sending evMonitorDetach: %x",
  663.                   ARRAYOFCONST((FClientID))));
  664.  
  665.     /* Tell the client we are detaching */
  666.     FClientEvent->SignalID(evMonitorDetach, FClientID);
  667.  
  668.     /* If we (the monitor) initiated the detach process, then wait around
  669.       for the client to acknowledge the detach, otherwise, just continue on */
  670.     if (Wait)
  671.       if (FMonitorEvent->WaitFor(TIMEOUT, FClientID, evClientDetach) == 0){
  672.         DbgStr(Format("Error waiting for client to detach: %x",
  673.                       ARRAYOFCONST((FClientID))));
  674.         FClientDirectory->RemoveClient(FClientID);
  675.       }
  676.     FClientID = 0;
  677.     FState = stDisconnected;
  678.     if (FOnConnect != 0)
  679.       FOnConnect(this, false);
  680.  
  681.     if ((! Wait) && (FOnDirUpdate != NULL))
  682.       DoOnDirUpdate();
  683.     }
  684. }
  685.  
  686. /* This method is called when the client has new data for us */
  687.  
  688. void __fastcall TIPCMonitor::DoOnSignal(void)
  689. {
  690.    if ((FOnSignal != NULL) && (FMonitorEvent->ID == FClientID))
  691.      FOnSignal(this, FMonitorEvent->Data);
  692. }
  693.  
  694. /* Tell the client we have new flags for it */
  695.  
  696. void __fastcall TIPCMonitor::SignalClient(const TClientFlags Value)
  697. {
  698.    if (FState == stConnected){
  699.       FClientEvent->EventInfo->FData.Flags = Value;
  700.       DbgStr("Signaling Client");
  701.       FClientEvent->SignalData(evMonitorSignal, FClientID, FClientEvent->Data);
  702.    }
  703. }
  704.  
  705. AnsiString __fastcall TIPCMonitor::GetClientName()
  706. {
  707.    return FClientDirectory->Name[FClientID];
  708. }
  709.  
  710. void __fastcall TIPCMonitor::GetClientNames(TStringList* List)
  711. {
  712.    int  I;
  713.    AnsiString  S;
  714.    int  DupCnt;
  715.  
  716.    List->BeginUpdate();
  717.    try{
  718.     List->Clear();
  719.     for(I=0; I < FClientDirectory->Count; I++){
  720.       S = FClientDirectory->ClientRec[I].Name;
  721.       DupCnt = 1;
  722.  
  723.       /* Number duplicate names so we can distinguish them in the client menu */
  724.       while(List->IndexOf(S) > -1){
  725.            DupCnt++;
  726.            S = Format("%s (%d)",
  727.                       ARRAYOFCONST((FClientDirectory->ClientRec[I].Name,
  728.                                     DupCnt)));
  729.       }
  730.       List->AddObject(S, (TObject*)FClientDirectory->ClientRec[I].ID);
  731.     }
  732.    }
  733.    catch(...){
  734.     List->EndUpdate();
  735.     throw;
  736.    }
  737.    List->EndUpdate();
  738. }
  739.  
  740. void __fastcall TIPCMonitor::SetCurrentClient(int ID)
  741. {
  742.    if (ID == 0)
  743.      ID = FClientDirectory->Last();
  744.    if (ID !=0)
  745.      FMonitorEvent->SignalID(evClientSwitch, ID);
  746. }
  747.  
  748. void __fastcall TIPCMonitor::ClearDebugInfo()
  749. {
  750. #ifdef IPCDEBUG
  751.    FTracer->Clear();
  752. #endif
  753. }
  754.  
  755. void __fastcall TIPCMonitor::GetDebugInfo(TStrings *List)
  756. {
  757. #ifdef IPCDEBUG
  758.    FTracer->GetList(List);
  759. #else
  760.    List->Add("Debug Tracing Disabled");
  761. #endif
  762. }
  763.  
  764. void __fastcall TIPCMonitor::SaveDebugInfo(const AnsiString FileName)
  765. {
  766. #if defined(IPCDEBUG)
  767.    TStringList*  List;
  768.  
  769.    List = new TStringList();
  770.    try{
  771.       GetDebugInfo(List);
  772.       List->SaveToFile(FileName);
  773.    }
  774.    catch(...){
  775.       delete List;
  776.       throw;
  777.    }
  778.    delete List;
  779. #endif
  780. }
  781.  
  782. void __fastcall TIPCMonitor::DoOnDirUpdate()
  783. {
  784.    if (FOnDirUpdate != NULL)
  785.      FOnDirUpdate(this);
  786. }
  787.  
  788. /* TIPCClient */
  789.  
  790. void __fastcall TIPCClient::Activate()
  791. {
  792.    if (FState == stInActive){
  793.      FWaitEvent = FConnectEvent;
  794.      FMonitorEvent->OwnerID = FID;
  795.      FConnectEvent->OwnerID = FID;
  796.      FClientEvent->OwnerID = FID;
  797.      FClientDirectory->AddClient(FID, FName);
  798.      FState = stDisconnected;
  799.      Resume();
  800.    }
  801. }
  802.  
  803. void __fastcall TIPCClient::DeActivate()
  804. {
  805.    if (FClientDirectory != NULL)
  806.      FClientDirectory->RemoveClient(FID);
  807.    if ((FState != stInActive) && !(Suspended)){
  808.       FWaitEvent->Signal(evClientExit);
  809.       if (WaitForSingleObject((void *)Handle, TIMEOUT) != WAIT_OBJECT_0)
  810.         TerminateThread((void *)Handle, 0);
  811.     }
  812. }
  813.  
  814. void __fastcall TIPCClient::Execute(void)
  815. {
  816.    DbgStr(FName + " Activated");
  817.    bool bContinueThread = true;
  818.  
  819.    if ((FClientDirectory->MonitorID) != 0)
  820.     {
  821.       FMonitorEvent->SignalID(evClientStart, FID);
  822.     }
  823.    while (bContinueThread == true){
  824.     try{
  825.       if (WaitForSingleObject(FWaitEvent->Handle, INFINITE) != WAIT_OBJECT_0)
  826.         break;
  827.       if (FWaitEvent->ID != FID){
  828.         Sleep(200);
  829.         continue;
  830.       }
  831.       DbgStr("Client Event Signaled: " + EventName(FWaitEvent->Kind));
  832.       switch(FWaitEvent->Kind){
  833.         case evMonitorSignal:
  834.           if (FOnSignal != NULL)
  835.             FOnSignal(this, FWaitEvent->Data);
  836.           break;
  837.         case evMonitorAttach:
  838.           ConnectToMonitor();
  839.           break;
  840.         case evMonitorDetach:
  841.           DisconnectFromMonitor(false);
  842.           Sleep(200);
  843.           break;
  844.         case evClientExit:
  845.           if (FClientDirectory->MonitorID != 0) {
  846.             if (FState == stConnected)
  847.               DisconnectFromMonitor(true);
  848.             else
  849.               FMonitorEvent->Signal(evClientStop);
  850.           }
  851.           bContinueThread = false;
  852.           break;
  853.         default:
  854.           break;
  855.       } //end switch
  856.     } //end try
  857.     catch(Exception& E){
  858.       DbgStr(Format("Exception raised in Thread Handler: %s at %X",
  859.                     ARRAYOFCONST((E.Message, ExceptAddr))));
  860.     }//end try-catch
  861.    } //end while
  862.    FState = stInActive;
  863.    DbgStr("Thread Handler Exited");
  864. }
  865.  
  866. void __fastcall TIPCClient::ConnectToMonitor()
  867. {
  868.    TEventData Data;
  869.    DbgStr("ConnectToMonitor Begin");
  870.    FConnectEvent->Reset();
  871.    try {
  872.      FState = stConnected;
  873.      FWaitEvent = FClientEvent;
  874.      Data.Flag = cfAttach;
  875.      FMonitorEvent->SignalData(evClientAttach, FID, Data);
  876.      if (FOnConnect != 0)
  877.        FOnConnect(this, true);
  878.    }
  879.    catch(Exception& E){
  880.      DbgStr("Exception in ConnectToMonitor: " + E.Message);
  881.      Data.Flag = cfError;
  882.      FMonitorEvent->SignalData(evClientAttach, FID, Data);
  883.    }
  884.    DbgStr("ConnectToMonitor End");
  885. }
  886.  
  887. void __fastcall TIPCClient::DisconnectFromMonitor(bool Wait)
  888. {
  889.    DbgStr("DisconnectFromMonitor Begin");
  890.    if (FState == stConnected) {
  891.      if (Wait){
  892.        DbgStr("Sending evClientDetach");
  893.        FMonitorEvent->Signal(evClientDetach);
  894.        if (FClientEvent->WaitFor(TIMEOUT, FID, evMonitorDetach))
  895.          DbgStr("Got evMonitorDetach");
  896.        else
  897.          DbgStr("Error waiting for evMonitorDetach");
  898.      }
  899.      FState = stDisconnected;
  900.      FWaitEvent = FConnectEvent;
  901.      if (! Wait){
  902.        DbgStr("DisconnectFromMonitor sending evClientDetach");
  903.        FMonitorEvent->Signal(evClientDetach);
  904.      }
  905.      if (FOnConnect != NULL)
  906.        FOnConnect((TIPCThread*)this, false);
  907.    }
  908.    DbgStr("DisconnectFromMonitor End");
  909. }
  910.  
  911. void __fastcall TIPCClient::SignalMonitor(const TEventData& Data)
  912. {
  913.    if (FState == stConnected){
  914.      DbgStr("Signaling Monitor");
  915.      FMonitorEvent->SignalData(evClientSignal, FID, Data);
  916.    }
  917. }
  918.  
  919. int __fastcall TIPCClient::ClientCount()
  920. {
  921.    return FClientDirectory->Count;
  922. }
  923.  
  924. void __fastcall TIPCClient::MakeCurrent()
  925. {
  926.    FMonitorEvent->SignalID(evClientStart, FID);
  927. }
  928.  
  929.